import { Injectable } from "@nestjs/common";
import { CreateUserDto } from "./dto/create-user.dto";
import { UpdateUserDto } from "./dto/update-user.dto";
import { Like, Not, Repository } from "typeorm";
import { InjectRepository } from "@nestjs/typeorm";
import { User } from "@/modules/user/entities/user.entity";
import { ResultData } from "@/common/utils/result";
import { instanceToPlain } from "class-transformer";
import { AppHttpCode } from "@/enum/code.enum";
import { JwtService } from "@nestjs/jwt";
import { CreateTokenDto } from "@/modules/user/dto/create-token.dto";
import { ConfigService } from "@nestjs/config";
import { Role } from "@/modules/role/entities/role.entity";
import { UserRole } from "@/modules/user-role/entities/user-role.entity";
import { before } from "@nestjs/swagger/dist/plugin";
import { UserSubuser } from "@/modules/user-subuser/entities/user-subuser.entity";
import { extractNestedPropertiesFromArray } from "@/common/utils/common";

@Injectable()
export class UserService {
    
    constructor(
        @InjectRepository(User)
        private readonly userRepository: Repository<User>,
        @InjectRepository(Role)
        private readonly roleRepository: Repository<Role>,
        @InjectRepository(UserRole)
        private readonly userRoleRepository: Repository<UserRole>,
        private readonly jwtService: JwtService,
        private readonly config: ConfigService,
        @InjectRepository(UserSubuser)
        private readonly userSubuserRepository: Repository<UserSubuser>
    ) {
    }
    
    async create(createUserDto: CreateUserDto, userId) {
        // 防止重复创建 start
        if (await this.findOneByUsername(createUserDto.username)) return ResultData.fail(AppHttpCode.USER_CREATE_EXISTING, "帐号已存在，请调整后重新注册！");
        //注入关系
        const user = await this.userRepository.save(createUserDto);
        await this.userSubuserRepository.save({ subuserId: user.id, userId });
        return ResultData.ok(instanceToPlain(user));
    }
    
    async findOneByUsername(username: string) {
        return await this.userRepository.findOne({ where: { username } });
    }
    
    async findOneById(id: number) {
        return await this.userRepository.findOne({
            where: { id },
            select: {
                id: true,
                username: true,
                status: true,
                password: true,
                userRole: {
                    id: true,
                    role: {
                        id: true,
                        roleApi: {
                            id: true,
                            api: {
                                apiUrl: true,
                                method: true
                            }
                        }
                    }
                },
                childUser: {
                    id: true,
                    subuser: {
                        id: true
                        // username: true
                    }
                }
            },
            relations: ["userRole.role.roleApi.api", "childUser.subuser"]
        });
    }
    
    async findAllNoPage({ userId }) {
        return ResultData.ok(instanceToPlain(await this.userRepository.find({
            order: {
                id: "DESC"
            },
            where: { id: Not(userId) }
        })));
    }
    
    async findChildAll(userId) {
        return ResultData.ok(
            extractNestedPropertiesFromArray(
                await this.userSubuserRepository.find({
                    order: {
                        id: "DESC"
                    },
                    where: { userId },
                    relations: ["subuser"]
                }))
        );
    }
    
    async findChildAllPage(dto) {
        const { page, size, userId } = dto;
        const take = size || 15;
        const skip = ((page || 1) - 1) * take;
        const where = {
            ...(userId ? { userId } : null)
            
        };
        const res = await this.userSubuserRepository.findAndCount({
            order: {
                id: "DESC"
            },
            where,
            relations: ["subuser"],
            skip,
            take
        });
        res[0] = extractNestedPropertiesFromArray(res[0]);
        return ResultData.rowOk(
            res
        );
    }
    
    async findAll(dto) {
        const { page, size, username, status, id } = dto;
        const take = size || 15;
        const skip = ((page || 1) - 1) * take;
        const where = {
            ...(id ? { id } : null),
            ...(status ? { status } : null),
            ...(username ? { username: Like(`%${username}%`) } : null)
        };
        return ResultData.rowOk(instanceToPlain(await this.userRepository.findAndCount({
            order: {
                id: "DESC"
            },
            where,
            select: {
                userRole: {
                    id: true,
                    role: {
                        id: true,
                        name: true,
                        remark: true
                    }
                },
                parentUser: {
                    id: true,
                    user: {
                        id: true,
                        username: true
                    }
                },
                childUser: {
                    id: true,
                    subuser: {
                        id: true,
                        username: true
                    }
                }
            },
            relations: ["userRole.role", "parentUser.user", "childUser.subuser"],
            skip,
            take
        })));
        // return this.userRepository.createQueryBuilder("user")
        // 	.orderBy("user.id", "DESC")
        // 	.leftJoinAndSelect("user.userRole", "userRole")
        // 	.leftJoinAndSelect("userRole.role", "role")
        // 	.select(["user.id", "user.username"])
        // 	.addSelect(["userRole.id"])
        // 	.addSelect(["role.name"])
        // 	// .where(`user.username like :username`, { username: `%${username}%` })
        // 	.andWhere(`user.status = :status`, { status: status?.trim() })
        // 	.skip(skip)
        // 	.take(take)
        // 	.getMany();
    }
    
    async update(id: number, updateUserDto: UpdateUserDto) {
        return ResultData.ok(await this.userRepository.update(id, updateUserDto), "更新成功");
    }
    
    async remove(id: number) {
        return ResultData.ok(await this.userRepository.delete(id), "删除成功");
    }
    
    /**
     * 生成 token 与 刷新 token
     * @param payload
     * @returns
     */
    genToken(payload: { id: string | number }): CreateTokenDto {
        const accessToken = `Bearer ${this.jwtService.sign(payload)}`;
        const refreshToken = this.jwtService.sign(payload, { expiresIn: this.config.get("JWT_REFRESH_EXPIRESIN") });
        return { accessToken, refreshToken };
    }
    
    /**
     * 生成刷新 token
     */
    refreshToken(id: string): string {
        return this.jwtService.sign({ id });
    }
    
    /** 校验 token */
    verifyToken(token: string): string {
        try {
            if (!token) return null;
            const id = this.jwtService.verify(token.replace("Bearer ", ""));
            return id;
        } catch (error) {
            return null;
        }
    }
    
    /**
     * 登录
     * username 有可能是 帐号/手机/邮箱
     */
    async login(username: string, password: string): Promise<ResultData> {
        const user = await this.findOneByUsername(username);
        if (!user) return ResultData.fail(AppHttpCode.USER_PASSWORD_INVALID, "帐号或密码错误");
        const checkPassword = password === user.password;
        if (!checkPassword) return ResultData.fail(AppHttpCode.USER_PASSWORD_INVALID, "帐号或密码错误");
        if (user.status === 0) return ResultData.fail(AppHttpCode.USER_ACCOUNT_FORBIDDEN, "您已被禁用，如需正常使用请联系管理员");
        // 生成 token
        const data = this.genToken({ id: user.id });
        return ResultData.ok(data, "登录成功");
    }
    
    async updateToken(userId: string): Promise<ResultData> {
        const data = this.genToken({ id: userId });
        return ResultData.ok(data);
    }
    
    async info(id) {
        return ResultData.ok(instanceToPlain(await this.userRepository.findOneById(id)));
    }
    
    async getUserMenu(id) {
        //取出menu并去重
        function extractMenus(data) {
            const menus = new Map();
            
            function extract(menu) {
                if (menu && menu.menu) {
                    const { id, ...rest } = menu.menu;
                    menus.set(id, { id, ...rest });
                }
                
                if (menu && menu.roleMenu && Array.isArray(menu.roleMenu)) {
                    menu.roleMenu.forEach((roleMenu) => extract(roleMenu));
                }
            }
            
            data.forEach((item) => extract(item.role));
            
            return Array.from(menus.values());
        }
        
        const res = await this.userRoleRepository.find({
            where: {
                userId: id
            },
            relations: ["role", "role.roleMenu.menu"]
        });
        // meta.btnPer = []
        const menuList = extractMenus(res);
        // return res
        const menuTypeISTowList = menuList.filter(menu => menu.menuType === 2);
        menuList.forEach((item) => {
            item.meta = {};
            item.meta.btns = [];
            menuTypeISTowList.forEach(menu => {
                // console.log("menu.menuKey",menu.menuKey)
                // console.log("menu.parentId",menu.parentId)
                // console.log("item.id",item.id)
                if (menu.parentId === item.id) {
                    item.meta.btns.push(menu.menuKey);
                }
            });
        });
        return ResultData.ok(menuList.filter(menu => menu.menuType !== 2).sort((a, b) => b.sort - a.sort));
    }
}
